/**
 * Project: dubbo.registry.console-2.1.0-SNAPSHOT
 * 
 * File Created at Sep 5, 2011
 * $Id: RestfuleUrlRewriter.java 181192 2012-06-21 05:05:47Z tony.chenl $
 * 
 * Copyright 1999-2100 Alibaba.com Corporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * Alibaba Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Alibaba.com.
 */
package com.alibaba.dubbo.governance.web.common.interceptor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import com.alibaba.citrus.service.requestcontext.rewrite.RewriteSubstitutionContext;
import com.alibaba.citrus.service.requestcontext.rewrite.RewriteSubstitutionHandler;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;

/**
 * Restful URL Rewrite成 WebX的URL。
 * 
 * @author ding.lid
 */
public class RestfuleUrlRewriter implements RewriteSubstitutionHandler {

    private static final Logger              logger              = LoggerFactory.getLogger(RestfuleUrlRewriter.class);

    private static final Map<String, String> pl2single;
    private static final Set<String>         appParameter;
    static {
        Map<String, String> map = new HashMap<String, String>();

        map.put("services", "service");
        map.put("users", "user");
        map.put("addresses", "address");
        map.put("applications", "application");

        map.put("providers", "provider");
        map.put("comsumers", "comsumer");

        pl2single = Collections.unmodifiableMap(map);
        Set<String> set = new HashSet<String>();

        set.add("_path");
        set.add("currentPage");
        set.add("_method");
        set.add("_type");
        set.add("id");

        appParameter = Collections.unmodifiableSet(set);
    }

    private final static String              METHOD_KEY          = "_method";                                         // show,
    private final static String              TYPE_KEY            = "_type";
    private final static String              ID_KEY              = "id";
    private final static String              PAGES_KEY           = "currentPage";
    private final static String              PATH_KEY            = "_path";

    private final static Pattern             SLASH_PATTERN       = Pattern.compile("/+");
    private final static Pattern             NUM_PATTERN         = Pattern.compile("\\d+");
    private final static Pattern             MULTI_NUM_PATTERN   = Pattern.compile("[+\\d]+");
    private final static Pattern             PAGES_SPLIT_PATTERN = Pattern.compile("/+pages/+");
    private final static Pattern             PAGES_PATTERN       = Pattern.compile(".*/+pages/+\\d+$");

    public void postSubstitution(RewriteSubstitutionContext context) {
        final String oldPath = context.getPath();
        String path = oldPath;
        while (path.startsWith("/")) {
            path = path.substring(1);
        }

        Map<String, String> param = new HashMap<String, String>();

        // 处理分页
        if (PAGES_PATTERN.matcher(path).matches()) {
            String[] page_path = PAGES_SPLIT_PATTERN.split(path);
            path = page_path[0];
            param.put(PATH_KEY, page_path[0]);
            param.put(PAGES_KEY, page_path[1]);
        } else {
            param.put(PATH_KEY, path);
        }
        List<String> temp = Arrays.asList(SLASH_PATTERN.split(path));
        //兼容2.0.x注册中心的shell风格url 如：http://root:hello1234@127.0.0.1:8080/status/dubbo.test.api.HelloService:1.1
        if("status".equals(temp.get(0))&&temp.size()>1){
            context.setPath("servicestatus");
            return;
        }
        //兼容包含group的path
        String[] split = (String[]) temp.toArray();
        
        if(temp.size()>2&&temp.contains("services")){
            List<String> parts = new ArrayList<String>();
            parts.addAll(temp);
            for(int i = 0;i<temp.size();i++){
                if ("services".equals(temp.get(i)) && i < (temp.size() - 1) && (!temp.get(i + 1).contains("."))&&(!temp.get(i + 1).matches("\\d+"))) {
                    String group = parts.get(i + 1);
                    String service = parts.get(i + 2);
                    parts.remove(i + 1);
                    parts.set(i+1, group + "/" + service);
                    break;
                }
            }
            split = parts.toArray(new String[parts.size()]);
        }
      
        int index = split.length;
        // module/action
        if (split.length < 2) return;
        // 最后一段不包含 '.'，如 .htm .xsd .css etc
        if (split[index - 1].contains(".")) {
            return;
        }

        final String type;
        // 偶数段
        // module/k/v/type or module/k/v/type/id/method
        if (index % 2 == 0) {
            if (MULTI_NUM_PATTERN.matcher(split[index - 2]).matches()) {
                // module/k/v/action/id/operate
                if (index < 4) return;

                param.put(METHOD_KEY, split[index - 1]);
                param.put(ID_KEY, split[index - 2]);
                type = split[index - 3];
                param.put(TYPE_KEY, type);
                index -= 3;
            } else {
                // module/k/v/type
                type = split[index - 1];
                param.put(TYPE_KEY, type);
                --index;
            }
        }
        // 奇数段
        // module/k/v/type/method or module/k/v/type/id
        else {
            if (index < 3) return;
            // module/k/v/type/id
            if (NUM_PATTERN.matcher(split[index - 1]).matches()) {
                param.put(ID_KEY, split[index - 1]);
            }
            // module/k/v/type/method
            else {
                param.put(METHOD_KEY, split[index - 1]);
            }
            type = split[index - 2];
            param.put(TYPE_KEY, type);
            index -= 2;
        }

        // 提取 KV对
        for (int i = 1; i < index; i += 2) {
            param.put(split[i], split[i + 1]);
        }

        String method = param.get(METHOD_KEY);
        
        String defaultRedirect = null;
        if(method == null || method.equals("index")){
            defaultRedirect = oldPath;
        }else{
            defaultRedirect = oldPath.split("/" + method)[0];
        }
        String id = param.get(ID_KEY);
        if(id != null){
            int i = defaultRedirect.lastIndexOf("/");
            defaultRedirect = defaultRedirect.substring(0,i);
        }
        context.getParameters().setString("defaultRedirect", defaultRedirect);
        
        final String module = split[0];
        context.setPath("/" + module + "/" + type + ".htm");

        for (Map.Entry<String, String> entry : param.entrySet()) {
            String key = entry.getKey();
            if (pl2single.containsKey(key)) {
                key = pl2single.get(key);
            } else if (appParameter.contains(key)) {
                // nothing
            } else {
                logger.info("key " + key + " is not pl noun!");
            }
            context.getParameters().setString(key, entry.getValue());
        }

        if (logger.isInfoEnabled()) {
            logger.info("REWRITE restful uri " + oldPath + " to uri " + module + "/" + type + ".htm?" + param);
        }
    }
}
